/*
 * StepBuilder.java
 *
 * Created on 22 de abril de 2006, 11:12
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package usecase;

import clasesbasicas.Paso;
import pattern.IPatternMatcher;
import pattern.IPatternReader;
import pattern.PatternMatcher;
import pattern.PatternReader;


import java.io.File;

import java.util.ArrayList;
import org.w3c.dom.Node;
import java.util.Hashtable;

import org.apache.log4j.*;

/**
 * This class builds a Step object from a Node object
 * It is called from UseCaseXML, due that class does not return any
 * XML element.
 *
 */
public class StepBuilder {
    
    //IStep s;
    Node nAux;
    ParticipantSet participants;
    protected Hashtable mainStepParticipants;
    IPatternReader prActors;
    
        
    /** Creates a new instance of StepBuilder */
    public StepBuilder () {
        this.mainStepParticipants = new Hashtable();
        this.participants = new ParticipantSet();
        
        File f = new File(
            configuration.Configuration.GetFileName ("ActorPatternFile")
         );
        if (f == null) {
            log.error ("File ActorPatternFile not found");
            return;
        }
        this.prActors = new PatternReader(f);
    }
    
    /** Builds a main step from a XML node
     **/
    public IStep builMainStep(Node n) {
        IStep s = this.buildStep(n);
        buildParticipant(s);
        return s;
    }

    /** Helpper method to generate the commons elements of a 
     * main and alternative steps
     **/
    IStep buildStep(Node n) {
        
        IStep s = new Step();
        
        buildId(s, n);
        buildDescription(s, n);        
        
        return s;
    }
    
    

    
    /** Builds and alternative or erroneous step from
     * a XML node
     *  Add the information of an alternative node
     * 1. Condition
     * 2. Action
     * 3. Result, if any.
     **/
    public IStep builAlternativeStep(Node n) {
        IStep s = this.buildStep (n);
        String[] info;
        
        // This is not used due the type is determined by the
        // iterator in UseCaseXML.
        s.setType (IStep.PRE);
        
        this.buildParticipantForAlternative(s);
        
        // This pattern file extartcs the parts of
        // an alternative step.
        File f = new File(
            configuration.Configuration.GetFileName ("AlternativePatternFile")
         );
        
        //System.out.println (new PatternReader(f).toString ());
        IPatternMatcher matcher;
        IPatternReader pReader = new PatternReader(f);
        matcher = new PatternMatcher();
        
        info = matcher.match (s.getDescription (), pReader);
        
        if (info == null) {
            String msg = "Unable to extract information from the step: <" + 
                        s.getId () + ": " + s.getDescription () + 
                        "> with patterns: \n" + new PatternReader(f).toString ();
            log.fatal (msg);
            throw new UseCaseXMLException(msg);
            //return s;
        }
        
        if (info.length < 2) {
            log.warn("Not enough information to build the alternative step. \n"
                     + "info: " + info.length 
                     + "\nfirst parent: " + info[0]);
        }
        
        if (info.length > 3) {
            log.warn("Too much information to build the alternative step. \n"
                     + "Maybe a wrong pattern: " + pReader.getPatternName ()
                     + "info: " + info.length 
                     + "\nfirst parent: " + info[0]);
        }
        
        s.setCondition (info[0]);
        s.setAction (info[1]);
        if (info.length > 2) 
            s.setConclution (info[2]);
            
        return s;
    }
    
    /** 
     * Get participant set
     **/
    public ParticipantSet getParticipants() {
        return this.participants;
    }

    
    //--- Helpper methods ------------------------------------------------
    
    /** Extracts the ide from the XML use case and stores it in
     * the step object.
     **/
    void buildId(IStep s, Node n) {
        Node nAux;
        
        nAux = n.getAttributes ().getNamedItem ("id");
        s.setId (nAux.getNodeValue ());
    }

    /** **/
    void buildDescription(IStep s, Node n) {
        //Node nAux;
        if (n.getFirstChild().getNodeType () != Node.TEXT_NODE) {
            System.err.println ("Error. No first child is text.");
        }
        s.setDescription (n.getFirstChild().getNodeValue ());
    }
    
    /**
     * This method takes a main step and identity the participant 
     * (actor o system) who performs the step. 
     * This method uses the pattern in ActorPatternFile file.
     * This method also adds the actor or the system into 
     * ParticipantSet
     * Participants are stored in lower case.
     *
     * @param IStep s is an input / oput parameter, it has to be 
     * inicialized but itis modified inside this method.
     **/
    void buildParticipant(IStep s) {
        String participant;
        int parentIndex;
        IPatternMatcher matcher;
        IPatternReader pReader;    
        String[] info;
        
        /**
         Si el objeto patterreader no se crea justo aqui, por ejemplo
         *en el constructor o en otra clas,
         *entonces no funciona porque tiene su buffer cerrado*/
        
        // Sera mas eficiente si esto lo hicieramos solo una vez

        File f = new File(
            configuration.Configuration.GetFileName ("ActorPatternFile")
         );
        if (f == null) {
            log.error ("File ActorPatternFile not found");
            return;
        }
        pReader = new PatternReader(f);

        
        //pReader = PatternReaderFactory.Get ("ActorPatternFile");
        matcher = new PatternMatcher();

        info = matcher.match (s.getDescription ().trim (), pReader);
        //info = matcher.match (s.getDescription ().trim (), this.prActors);
        if (info == null) {
            log.error ("No pattern match for " + s.getDescription () 
              + " in ActorPatternFile.");
            return;
        }
        
        // Always takes the second element, unless you change the pattern.
        if (info.length < 1) {
            log.error ("Not enought information after matching pattern in '" +
                s.getDescription ()+"'. " +
                "Elements found: " + info.length);
            return;
        }
        
        parentIndex = info.length - 1;
        
        if (info[parentIndex] == null) {
            log.error ("No actor nor system found in pattern. " +
                "Elements found: " + info.length);
            return;
        }
        
        participant = info[parentIndex].toLowerCase ();
        this.participants.add (participant);
        s.setParticipant (participant);
        log.debug ("Adding participant '" + participant + 
            "' with key '" + s.getId ()+"'");
        this.mainStepParticipants.put (s.getId (), participant);
    }

    
    /** I need to identfy the participant in a fragment of an 
     * alternative step.
     *It would be a great idea refactor this method and the upeer
     *and downerr methods.
     *
     * Returns null if no participant was found
     **/
    public String identifyParticipant (String text) {
        IStep s_temp = new Step();
        s_temp.setParticipant (null);
        s_temp.setId ("TMP");
        s_temp.setDescription (text);
        this.buildParticipant (s_temp);
        return s_temp.getParticipant ();
    }
    
    /**
     * Takes an alternative step and identity the participant 
     * (actor o system) who performs the step. 
     * This method uses the participans of the main step.
     **/
    void buildParticipantForAlternative(IStep s) {
        String parentId = StepBuilder.GetParentStep (s.getId ());
        String p = (String) this.mainStepParticipants.get (parentId);
        
        if (p == null) {
            log.warn ("No participant for alternative step '" + s.getId ()
                +"' / parentId '" + parentId+"'");
        }
        s.setParticipant (p);
        return ;
    }

    
    //--- Helpper Static methods -----------------------------------------
    
    /** Returns the parent step of the step 
     *  Id must follow this pattern: X.Y..... where X
     * is the Id of the parent step.
     *
     * @return null if id does not follow the pattern.
     **/
    public static String GetParentStep(String id) {
        String s[];
        s = id.split ("\\.");
        if (s.length == 0 )
            return null;
        return s[0];
    }
    
    /** Builds and alternative method or erroneous method step from
     * a XML node
     *  Add the information of an alternative node
     * 1. Condition
     * 2. Action
     * 3. Result, if any.
     **/
    public IStep builAlternativeMethodStep(Node n) {
        IStep s = this.buildStep (n);
        String[] info;
        
        // This is not used due the type is determined by the
        // iterator in UseCaseXML.
        s.setType (IStep.PRE);    
       
        
        info = this.matcher(s.getDescription ());
        
        if (info == null) {
            String msg = "Unable to extract information from the step: <" + 
                        s.getId () + ": " + s.getDescription () + 
                        "> with patterns";
            log.fatal (msg);
            throw new UseCaseXMLException(msg);
            //return s;
        }
        
        if (info.length < 2) {
            log.warn("Not enough information to build the alternative step. \n"
                     + "info: " + info.length 
                     + "\nfirst parent: " + info[0]);
        }
        
        if (info.length > 3) {
            log.warn("Too much information to build the alternative step. \n"
                     + "info: " + info.length 
                     + "\nfirst parent: " + info[0]);
        }
        
        s.setCondition (info[0]);
        s.setAction (info[1]);
        if (info.length > 2) 
            s.setConclution (info[2]);
            
        return s;
    }
    
    public String[] matcher(String texto){
        
        String[] info = new String[2];
        String msg = texto;
        log.warn(msg);
        texto = texto.replaceAll("&&", "and");        
        texto = texto.replaceAll("\\|\\|", "or");
        texto = texto.replace("/* @Pre */", "");
        texto = texto.replace("/* @Post */", "");
        texto = texto.replace("(AVL)", "");
        texto = texto.replace("\n", "");
        
        Integer posinicio = texto.indexOf("(");
        if (posinicio != -1){
            Integer posfinal = texto.indexOf(";");
            info[0] = texto.substring(posinicio, posfinal);
            String efecto = texto.substring(posfinal+1, texto.length()-1);
            info [1] = efecto;
        }
        
        return info;
    
    }

    
    //--- Log ------------------------------------------------------------
    static Logger log;
    static {
        log = Logger.getLogger (StepBuilder.class.getName());
    }

}
